package system

import (
	"encoding/gob"
	"encoding/json"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/facade/runtime"
	"gitee.com/sansaniot/ssiot-core/logger"

	"ssdevice/common/constant"
	"ssdevice/common/database"
	"ssdevice/common/dto"
	"ssdevice/common/enums"
	mq "ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/data/action"
	"ssdevice/internal/data/partition"
	"ssdevice/internal/data/task"
	"ssdevice/internal/data/thread"
	"ssdevice/internal/data/thread/inner"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/command"
	"ssdevice/internal/device/models/query"
	"ssdevice/internal/device/service"
	ruletask "ssdevice/internal/rule/task"
)

func InitSystemBiz() {
	// 数据初始
	initDb()
	// 缓存初始化
	initSystemCache()
	// 启动数据线程
	initDataThread()
	// 启动mqtt客户端
	initMqttClient()
	// 注册业务类
	initRegistryPartition()
	// 规则引擎任务
	ruletask.Start()
	// 定时任务
	task.Start()

}

func initRegistryPartition() {
	// 获取db
	db := runtime.Runtime.GetDbByKey("db")
	if db == nil {
		return
	}
	// 登录
	loginAction := &action.Login{Orm: db}
	partition.Registry(enums.Login.Get().Name, loginAction)
	loginAction.Run("LoginThread")
	// 登出
	partition.Registry(enums.Logout.Get().Name, &action.Logout{Orm: db})
	// 属性
	partition.Registry(enums.Props.Get().Name, &action.Props{Orm: db})
	// 变量
	partition.Registry(enums.Sensor.Get().Name, &action.Sensor{Orm: db})
	// 控制
	partition.Registry(enums.Func.Get().Name, &action.Funcs{Orm: db})
	// 配置
	partition.Registry(enums.Config.Get().Name, &action.Config{Orm: db})
	// 升级
	partition.Registry(enums.Ota.Get().Name, &action.Ota{Orm: db})
}

func initDataThread() {
	// 历史数据
	thread.DevHisDataThread{}.Run("DevHisDataThread")
	// sensor更新
	thread.DevSensorUpdateThread{}.Run("DevSensorUpdateThread")
	// sensor插入
	thread.DevSensorInsertThread{}.Run("DevSensorInsertThread")
	// dev状态更新
	thread.DevStatusUpdateThread{}.Run("DevStatusUpdateThread")
	// 明细入库
	thread.DataDetailThread{}.Run("DataDetailThread")
	// logout
	thread.LogoutTimeoutThread{}.Run("LogoutTimeoutThread")
	// system
	inner.DevHisSystemThread{}.Run("DevHisSystemThread")
}

func initMqttClient() {
	mq.MqttClient = mq.NewClient()
	// 固定订阅主题
	mq.MqttClient.InitSubscribe("/iot/"+constant.TopicPlaceholder+"/+/login", mq.TopicHandler)
	mq.MqttClient.InitSubscribe("/iot/"+constant.TopicPlaceholder+"/+/logout", mq.TopicHandler)
	mq.MqttClient.InitSubscribe("/iot/"+constant.TopicPlaceholder+"/+/props", mq.TopicHandler)
	mq.MqttClient.InitSubscribe("/iot/"+constant.TopicPlaceholder+"/+/sensor", mq.TopicHandler)
	mq.MqttClient.InitSubscribe("/iot/"+constant.TopicPlaceholder+"/+/config/#", mq.TopicHandler)
	mq.MqttClient.InitSubscribe("/iot/"+constant.TopicPlaceholder+"/+/ota/#", mq.TopicHandler)
	mq.MqttClient.InitSubscribe("/iot/"+constant.TopicPlaceholder+"/+/func", mq.TopicHandler)
	if _, err := mq.MqttClient.CreateClient(); err != nil {
		logger.Error("mqtt连接失败" + err.Error())
	}
}

func initSystemCache() {
	// 设备缓存初始与清理
	devCache()
	// sensor本地缓存
	sensorCache()
	// 产品本地缓存
	productCache()
	// protocol缓存
	protocolCache()

	// 清理redis待响应消息队列
	_ = env.Cache.Del(constant.DeviceLoginCacheQueue)

	// 历史并发数
	var parallel = env.ConfigOperator.GetInt("service.device.parallel")
	if parallel > 0 {
		constant.DataRoutineChild = parallel
	}

	// raw sql批量大小
	var batchSize = env.ConfigOperator.GetInt("service.device.batchsize")
	if batchSize > 0 {
		constant.ThreadBatchSize = batchSize
	}

	// 缓存持久化间隔秒
	var cacheFlush = env.ConfigOperator.GetInt("service.device.cacheflush")
	if cacheFlush > 0 {
		constant.CacheFlushSecInterval = cacheFlush
	}

	// 注册gob支持的类型
	gob.Register(map[string]interface{}{})
	gob.Register([]interface{}{})

	// 确定设备协议字段(devCache之后)
	if err := service.UpdateDevProtocol(env.Db); err != nil {
		logger.Errorf("刷新设备协议异常,%v", err)
	}
}

func devCache() {
	if env.Db == nil {
		return
	}
	// 查所有设备
	devModel := &models.SysDev{}
	devModel.Orm = env.Db
	devList := make([]models.SysDev, 0)
	if err := devModel.FindList(&devList); err != nil {
		logger.Error(err)
		return
	}
	cacheMap := map[string]interface{}{}
	cacheMap2 := map[string]interface{}{}
	gateDevs := make(map[string][]string)
	for _, v := range devList {
		m := map[string]interface{}{
			"type":         v.Type,
			"name":         v.Name,
			"productModel": v.ProductModel,
			"gateway":      v.GatewaySn,
		}
		cacheMap[v.Sn] = string(utils.MapToBytes(m))
		cacheMap2[v.Sn] = v.DeptId
		// 关系
		if len(v.GatewaySn) > 0 && v.GatewaySn != v.Sn {
			if _, ok := gateDevs[v.GatewaySn]; !ok {
				gateDevs[v.GatewaySn] = make([]string, 0)
			}
			gateDevs[v.GatewaySn] = append(gateDevs[v.GatewaySn], v.Sn)
		} else if len(v.ProductModel) > 0 && v.Type != enums.DevTypeOfDevice {
			constant.GateModelCache.Set(v.Sn, v.ProductModel)
		}
		// 状态
		if v.OnlineStatus == enums.Online && v.LastLiveTime != nil {
			constant.DevOnlineCache.Set(v.Sn, utils.FormatTimeToStr(time.Time(*v.LastLiveTime)))
		}
	}
	// 设备缓存与清理
	_ = env.Cache.Del(constant.DeviceEasyCache)
	_ = env.Cache.Del(constant.DeviceDeptCache)
	_ = env.Cache.HashSet(constant.DeviceEasyCache, cacheMap)
	_ = env.Cache.HashSet(constant.DeviceDeptCache, cacheMap2)
	// 设备-网关关系缓存
	for gateSn, devSns := range gateDevs {
		constant.GateDevsCache.Set(gateSn, devSns)
		for _, sn := range devSns {
			constant.DevGateCache.Set(sn, gateSn)
		}
	}
}

func sensorCache() {
	if env.Db == nil {
		return
	}
	// 查所有设备(查sn、data减少启动查询时间)
	sensorList := make([]models.SysDevSensor, 0)
	if err := env.Db.Model(&models.SysDevSensor{}).Select([]string{"sn", "data"}).Scan(&sensorList).Error; err != nil {
		logger.Error(err)
		return
	}
	snDataMap := make(map[string]map[string]interface{})
	for _, sensor := range sensorList {
		if _, ok := snDataMap[sensor.Sn]; !ok {
			snDataMap[sensor.Sn] = make(map[string]interface{})
		}
		utils.DeepMerge(utils.BytesToMap(sensor.Data), snDataMap[sensor.Sn])
	}
	// 初始缓存
	for sn, sensorMap := range snDataMap {
		constant.SensorCache.Set(sn, sensorMap)
	}
}

func productCache() {
	defaultLogoutTimeout := env.ConfigOperator.GetInt("service.device.logoutTimeout")

	go func() {
		for {
			devConfigModel := &models.SysDevConfig{}
			devConfigModel.Orm = env.Db
			err, list := devConfigModel.FindAllProductConfigs()
			if err != nil {
				return
			}

			for _, v := range list {
				dataMap := map[string]interface{}{}
				err = json.Unmarshal(v.Data, &dataMap)
				if err != nil {
					continue
				}

				productConfig := &constant.ProductConfig{}

				if defaultLogoutTimeout > 0 {
					productConfig.LogoutTimeout = defaultLogoutTimeout
				}

				if logoutTimeoutAny, ok := dataMap["config"].(map[string]interface{})["logoutTimeout"]; ok {
					if logoutTimeout, ok1 := logoutTimeoutAny.(float64); ok1 {
						productConfig.LogoutTimeout = int(logoutTimeout)
					} else {
						env.Log.Debugf("product %s logoutTimeout is not a number ", v.ProductModel)
					}
				}

				constant.ProductConfigCache.Store(v.ProductModel, productConfig)
			}

			time.Sleep(5 * time.Second)
		}
	}()
}

func protocolCache() {
	sysProtocol := models.SysProtocol{}
	m := sysProtocol.FindGateModelMap(env.Db)
	for md, protocol := range m {
		if protocol.Value == nil {
			continue
		}
		constant.ModelProtocolCache.Set(md, *protocol.Value)
	}
}

func initDb() {
	// 默认产品初始确认
	pdmodel := &models.SysDevProduct{}
	pdmodel.Orm = env.Db
	_ = pdmodel.FindOne(pdmodel, dto.MakeCondition(query.SysProductQuery{ProductModel: constant.DefaultProductCode}))
	if len(pdmodel.Id) == 0 {
		pdservice := &service.Product{}
		pdservice.Orm = env.Db
		pdservice.SetAllModel()
		if err, _ := pdservice.AddProduct(&command.ProductInsertReq{
			Name:         "默认产品",
			DeviceType:   enums.DevTypeOfDevice,
			ProductModel: constant.DefaultProductCode,
			DeptId:       constant.PublicGroupId,
			Photo:        "",
			Description:  "",
			View:         nil,
			DevConfig:    &models.SysDevConfig{Data: []byte(constant.DevDefaultConfigTemplate), Schema: []byte(constant.DevDefaultConfigSchemaTemplate)},
			DevFunc:      &models.SysDevFunc{Data: []byte(constant.DevDefaultFuncTemplate), Schema: []byte(constant.DevDefaultFuncSchemaTemplate)},
		}); err != nil {
			logger.Errorf("初始化默认产品异常,%v", err)
		}
	}
	// 启动历史数据分区表
	var partitionFlag = env.ConfigOperator.GetBool("service.device.partition")
	if partitionFlag {
		partType := env.ConfigOperator.GetString("service.device.parttype")
		database.PartitionTb(env.Db, partType)
	}
}
